<!--
 * @description 选项卡组件
 * @author: 陈陈
 * @date: 2025/05/09
 * @phone: 18560000860
 * @email: 18560000860@qq.com
 * @company: 济南晨霜信息技术有限公司 /
 * @business: 承接前后端项目的开发
-->
<template>
	<view class="pure-tabs" :class="_classes" :style="_styles">
		<!-- Left -->
		<view class="pure-tabs__left" v-if="$slots.left">
			<slot name="left"></slot>
		</view>

		<!-- Content -->
		<view class="pure-tabs__content">
			<scroll-view
				class="pure-tabs__scroll-view pure-hide-scrollbar"
				:scroll-x="_isScrollX"
				:scroll-y="_isScrollY"
				:scroll-left="_scrollLeft"
				:scroll-top="_scrollTop"
				:scroll-with-animation="animation"
				@scroll="_onScroll"
			>
				<view class="pure-tabs__items">
					<view
						class="pure-tabs__item"
						v-for="(item, index) in list"
						:key="index"
						:class="{
							'pure-tabs__item--active': index === current,
							'pure-tabs__item--disabled': item?.disabled,
						}"
						@tap="_onTabClick(index, item)"
					>
						<slot
							:item="item"
							:active="index === current"
							:index="index"
						>
							<text class="pure-tabs__item__text">{{
								item[labelKey]
							}}</text>
						</slot>
					</view>
					<view
						class="pure-tabs__indicator"
						:style="
							vertical
								? { top: _indicatorTop + 'px' }
								: { left: _indicatorLeft + 'px' }
						"
					></view>
				</view>
			</scroll-view>
		</view>

		<!-- Right -->
		<view class="pure-tabs__right" v-if="$slots.right">
			<slot name="right"></slot>
		</view>
	</view>
</template>

<script>
import commonComponentOptions from "../../config/common-component-options";
export default {
	options: commonComponentOptions,
};
</script>

<script setup>
import {
	ref,
	computed,
	onMounted,
	getCurrentInstance,
	nextTick,
	watchEffect,
} from "vue";
import props from "./props";
import { getDOMRect } from "../../functions/dom";

// **************************************************************************************************************
// * Refs
// **************************************************************************************************************
// 组件实例
const _instance = getCurrentInstance();
// 滚动位置
const _scrollLeft = ref(0);
const _scrollTop = ref(0);
// 是否滚动中
let _scrolling = null;
// 指示条的位置
const _indicatorLeft = ref(null);
const _indicatorTop = ref(null);

// **************************************************************************************************************
// * Options
// **************************************************************************************************************
defineOptions(commonComponentOptions);

// **************************************************************************************************************
// * Props
// **************************************************************************************************************
const _props = defineProps(props);

// **************************************************************************************************************
// * Emits
// **************************************************************************************************************
const _emits = defineEmits(["change"]);

// **************************************************************************************************************
// * Classes
// **************************************************************************************************************
const _classes = computed(() => {
	const array = [];

	// 滚动方向
	if (_isScrollX.value) array.push("pure-tabs--scroll-x");
	if (_isScrollY.value) array.push("pure-tabs--scroll-y");

	// 禁止滚动zi
	if (_props.disabled) array.push("pure-tabs--disabled");

	// 等分布局
	if (_props.equal) array.push("pure-tabs--equal");

	// 两端对齐布局
	if (_props.justify) array.push("pure-tabs--justify");

	return array;
});

// **************************************************************************************************************
// * Styles
// **************************************************************************************************************
const _styles = computed(() => {
	const array = [];
	return array;
});

// **************************************************************************************************************
// * Watchers
// **************************************************************************************************************
watchEffect(() => {
	// 解构数组，不使用变量名，仅为触发依赖收集
	[_props.list, _props.vertical, _props.current];

	// 重新布局
	_calcLayout();
});

// **************************************************************************************************************
// * Computed
// **************************************************************************************************************
// 是否横向滚动
const _isScrollX = computed(() => {
	// 设置为纵向滚动
	if (_props.vertical) return false;
	return true;
});

// 是否纵向滚动
const _isScrollY = computed(() => {
	// 纵向滚动开启状态
	return _props.vertical;
});

// **************************************************************************************************************
// * 生命周期
// **********************************************************************************************************
onMounted(async () => {
	// 计算布局
	_calcLayout();
});

// **************************************************************************************************************
// * Functions
// **************************************************************************************************************
// 计算布局
async function _calcLayout() {
	// 等待数据渲染完成
	await nextTick();

	// 获取滚动容器的布局信息
	const scrollViewRect = await getDOMRect(
		".pure-tabs__scroll-view",
		_instance.proxy
	);

	// 未成功获取到滚动容器的布局信息
	if (!scrollViewRect)
		return console.log("PureTabs： 未成功获取到滚动容器的布局信息。");

	// 获取列表容器的布局信息
	const listRect = await getDOMRect(".pure-tabs__items", _instance.proxy);

	// 未成功获取到列表容器的布局信息
	if (!listRect)
		return console.log("PureTabs： 未成功获取到列表容器的布局信息。");

	// 获取当前激活项的布局信息
	const activeItemRect = await getDOMRect(
		".pure-tabs__item--active",
		_instance.proxy
	);

	// 未成功获取到当前激活项的布局信息
	if (!activeItemRect)
		return console.log("PureTabs： 未成功获取到当前激活项的布局信息。");

	// 吸附贴边模式
	if (_props.sticky) {
		// 滚动到容器左侧
		_scrollToLeft(scrollViewRect, listRect, activeItemRect);
		return;
	}

	// 只将激活项滚动到可视区域模式
	if (_props.visible) {
		// 滚动到可视区域,即激活项右侧和容器右侧对齐
		_scrollToVisible(scrollViewRect, listRect, activeItemRect);
		return;
	}

	// 默认滚动到居中
	_scrollToCenter(scrollViewRect, listRect, activeItemRect);
}

// 滚动到居中
function _scrollToCenter(scrollViewRect, listRect, activeItemRect) {
	// 横向滚动
	if (!_props.vertical) {
		// 滚动容器的布局信息
		const { width: scrollViewWidth } = scrollViewRect;

		//  列表容器的布局信息
		const { left: listLeft } = listRect;

		// 当前子元素的布局信息
		const { width: itemWidth, left: itemLeft } = activeItemRect;

		// 中间点
		const centerPoint = scrollViewWidth / 2;

		// 应该滚动到的位置
		const scrollTo = itemLeft - listLeft - centerPoint + itemWidth / 2;

		// 滚动
		_scrollLeft.value = scrollTo;

		// 指示条的位置
		_indicatorLeft.value = itemLeft - listLeft + itemWidth / 2;

		return;
	}

	// 纵向滚动
	// 滚动容器的布局信息
	const { height: scrollViewHeight } = scrollViewRect;

	//  列表容器的布局信息
	const { top: listTop } = listRect;

	// 当前子元素的布局信息
	const { height: itemHeight, top: itemTop } = activeItemRect;

	// 中间点
	const centerPoint = scrollViewHeight / 2;

	// 应该滚动到的位置
	const scrollTo = itemTop - listTop - centerPoint + itemHeight / 2;

	// 滚动
	_scrollTop.value = scrollTo;

	// 指示条的位置
	_indicatorTop.value = itemTop - listTop + itemHeight / 2;
}

// 滚动到容器最左侧
function _scrollToLeft(scrollViewRect, listRect, activeItemRect) {
	// 横向滚动
	if (!_props.vertical) {
		//  列表容器的布局信息
		const { left: listLeft } = listRect;

		// 当前子元素的布局信息
		const { width: itemWidth, left: itemLeft } = activeItemRect;

		// 应该滚动到的位置
		const scrollTo = itemLeft - listLeft;

		// 滚动
		_scrollLeft.value = scrollTo;

		// 指示条的位置
		_indicatorLeft.value = itemLeft - listLeft + itemWidth / 2;

		return;
	}

	// 纵向滚动
	//  列表容器的布局信息
	const { top: listTop } = listRect;

	// 当前子元素的布局信息
	const { height: itemHeight, top: itemTop } = activeItemRect;

	// 应该滚动到的位置
	const scrollTo = itemTop - listTop;

	// 滚动
	_scrollTop.value = scrollTo;

	// 指示条的位置
	_indicatorTop.value = itemTop - listTop + itemHeight / 2;
}

// 滚动到可视区域,即激活项右侧和容器右侧对齐
function _scrollToVisible(scrollViewRect, listRect, activeItemRect) {
	// 横向滚动
	if (!_props.vertical) {
		// 滚动容器的布局信息
		const {
			width: scrollViewWidth,
			left: scrollViewLeft,
			right: scrollViewRight,
		} = scrollViewRect;

		//  列表容器的布局信息
		const { left: listLeft } = listRect;

		// 当前子元素的布局信息
		const {
			width: itemWidth,
			left: itemLeft,
			right: itemRight,
		} = activeItemRect;

		// 先判断右侧是否不可见
		if (itemRight > scrollViewRight) {
			// 应该滚动到的位置
			const scrollTo = itemLeft - listLeft - scrollViewWidth + itemWidth;

			// 滚动
			_scrollLeft.value = scrollTo;
		}

		// 在判断左侧是否不可见
		else if (itemLeft < scrollViewLeft) {
			// 应该滚动到的位置
			const scrollTo = itemLeft - listLeft;

			// 滚动
			_scrollLeft.value = scrollTo;
		}

		// 指示条的位置
		_indicatorLeft.value = itemLeft - listLeft + itemWidth / 2;

		return;
	}

	// 纵向滚动
	// 滚动容器的布局信息
	const {
		height: scrollViewHeight,
		top: scrollViewTop,
		bottom: scrollViewBottom,
	} = scrollViewRect;

	//  列表容器的布局信息
	const { top: listTop } = listRect;

	// 当前子元素的布局信息
	const {
		height: itemHeight,
		top: itemTop,
		bottom: itemBottom,
	} = activeItemRect;

	// 先判断底部是否不可见
	if (itemBottom > scrollViewBottom) {
		// 应该滚动到的位置
		const scrollTo = itemTop - listTop - scrollViewHeight + itemHeight;

		// 滚动
		_scrollTop.value = scrollTo;
	}

	// 再判断顶部是否不可见
	else if (itemTop < scrollViewTop) {
		// 应该滚动到的位置
		const scrollTo = itemTop - listTop;

		// 滚动
		_scrollTop.value = scrollTo;
	}

	// 指示条的位置
	_indicatorTop.value = itemTop - listTop + itemHeight / 2;
}

// 滚动事件
function _onScroll() {
	// 滚动状态为正在滚动
	if (_scrolling) {
		clearTimeout(_scrolling);
		_scrolling = null;
	}

	// 设置新的定时器
	_scrolling = setTimeout(() => {
		clearTimeout(_scrolling);
		_scrolling = null;
	}, 100);
}

// 子项点击事件
function _onTabClick(index, item) {
	// 下标一致
	if (index === _props.current) return;

	// 正在滚动中
	if (_scrolling !== null) return;

	// 触发事件
	_emits("change", index, item);
}
</script>

<style scoped lang="scss">
@import "./style.scss";
</style>
